Aller au contenu principal

Les goroutines et channels

Les goroutines

Une goroutine est une fonction qui s'exécute en parallèle d'autres fonctions, elle est donc asynchrone.

Pour créer une goroutine il suffit d'ajouter le mot clé go devant l'appel de la fonction.

Exemple :

package main

import (
"fmt"
"time"
)

func main() {
// Exécution de la fonction sayHello avec une goroutine
go printNumbers()

// Exécution d'une fonction sans goroutine
for i := 0; i <= 5; i++ {
fmt.Println("Main function", i)
time.Sleep(1 * time.Second)
}

// On quelques secondes pour que la goroutine se termine
time.Sleep(3 * time.Second)

}

func printNumbers() {
for i := 0; i <= 5; i++ {
fmt.Println(i)
time.Sleep(1 * time.Second)
}
}

Dans cet exemple la fonction printNumbers est exécutée en parallèle de la fonction main. Les deux fonctions s'exécutent donc en même temps, en l'éxecutant vous devriez obtenir le résultat suivant ou un résultat similaire :

0
Main function 0
1
Main function 1
2
Main function 2
3
Main function 3
4
Main function 4
5
Main function 5

Les deux fonctions s'éxécutent donc bien en parallèle. A noter qu'ici nous avons utiliser la fonction Sleep du package time à la fin de la fonction main pour attendre que la goroutine se termine. Dans un cas réel il faudra utiliser des channels pour attendre la fin de l'exécution d'une goroutine. Ce que nous allons voir à la suite.

Les channels

Un channel (canal en français) est un mécanisme de communication et de synchronisation qui permet l'échange de données entre goroutines. Il agit comme un tuyau dans lequel on peut envoyer et recevoir des données.

Un channel peut être utilisé pour envoyer des données d'une goroutine à une autre de manière synchronisée. Cela signifie que lorsque qu'une goroutine envoie des données dans un channel, elles seront bloquées jusqu'à ce qu'une autre goroutine les récupère. De même lorsque qu'une goroutine tente de récupérer des données dans un channel, elle sera bloquée jusqu'à ce qu'une autre goroutine envoie des données dans le channel.

Créer un channel

Pour créer un channel il suffit d'utiliser la fonction make du package make suivi du mot clé chan et du type de données que l'on souhaite envoyer dans le channel.

Exemple :

package main

func main() {
// Création d'un channel de type string
channel := make(chan string)
}

Utiliser un channel

Pour envoyer des données dans un channel il suffit d'utiliser le mot clé chan suivi du nom du channel et du mot clé <- suivi des données à envoyer. Pour récupérer des données dans un channel il suffit d'utiliser le mot clé <- suivi du nom du channel.

Exemple :

package main

import (
"fmt"
"time"
)

func main() {
// Création d'un channel de type int
intChannel := make(chan int)

// Goroutine qui envoie une donnée dans le channel
go func() {
// Envoi d'une donnée dans le channel
intChannel <- 16
}()

// Récupération de la donnée dans le channel
value := <-intChannel

fmt.Println(value) // Affiche la valeur reçue : 16
}

Dans cet exemple nous avons créé un channel de type int puis nous avons créé une goroutine qui envoie la valeur 16 dans le channel. Ensuite nous récupérons la valeur envoyée dans le channel et nous l'affichons.

Le mot clé "defer"

Le mot clé defer permet de reporter l'exécution d'une fonction jusqu'à la fin de l'exécution de la fonction courante. Il est souvent utilisé pour fermer des fichiers ou des channels.

Exemple :

package main

import (
"fmt"
"time"
)

func main() {
// Création d'un channel de type int
intChannel := make(chan int)

// Goroutine qui envoie une donnée dans le channel
go func() {
// Envoi d'une donnée dans le channel
intChannel <- 16
}()

// Récupération de la donnée dans le channel
value := <-intChannel

fmt.Println(value) // Affiche la valeur reçue : 16

// Fermeture du channel
defer close(intChannel)
}

Dans cet exemple nous avons utilisé le mot clé defer pour fermer le channel à la fin de l'exécution de la fonction main.

Les channels bufferisés

Un channel bufferisé est un channel qui peut contenir plusieurs valeurs.

Pour créer un channel bufferisé il suffit d'utiliser la fonction make du package make suivi du mot clé chan et du type de données que l'on souhaite envoyer dans le channel suivi du nombre de valeurs que l'on souhaite pouvoir stocker dans le channel.

Exemple :

package main

import (
"fmt"
"time"
)

func main() {
// Création d'un channel bufferisé de type int
intChannel := make(chan int, 2)

// Envoi de deux valeurs dans le channel
intChannel <- 16
intChannel <- 32

// Récupération des deux valeurs dans le channel
value1 := <-intChannel
value2 := <-intChannel

fmt.Println(value1) // Affiche la valeur reçue : 16
fmt.Println(value2) // Affiche la valeur reçue : 32
}